package phaseII;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Stack;

public class Scanner {

	private java.util.Scanner fileReader = null;
	//private boolean tokenGrabbed = false;
	private boolean scanningRegex = false;
	private boolean scanningFilenames = false;
	
	private int lineNumber = 0;
	private int columnNumber = 0;
	private int totalFileLines = 0;
	
	private char[] currentLine = null;
	
	
	private StringBuffer strBuffer = new StringBuffer();
	
	private String nextToken = "";
	private final String SPECIALCASES = ";#(),";
	
	private TokenType nextTokenType = null;
	
	private Stack<Character> parenStack = new Stack<Character>();
	
	public Scanner(String miniREFilePath) {
		// TODO Scan miniRE file and store tokens and their types (keyword, vriable, etc..)
		try {
			File miniREFile = new File(miniREFilePath);
			fileReader = new java.util.Scanner(miniREFile);
			//count lines in file
			while(fileReader.hasNextLine()) {
				fileReader.nextLine();
				totalFileLines++;
			}
			//reset the scanner
			fileReader = new java.util.Scanner(miniREFile);
		} catch (FileNotFoundException fnfe) {
			System.out.println(fnfe);
		}
	}
	
	public String getToken() {
		if(nextToken=="") {
			determineNextToken();
		}
		String currentToken = nextToken;
		determineNextToken();
		return currentToken;
	}
	
	public String peekToken() {
		if(nextToken==""){
			determineNextToken();
		}
		return nextToken;
	}
	
	public TokenType peekTokenType() {
		peekToken();
		return nextTokenType;
	}
	
	private TokenType checkTokenType(String token) {
		//if first token not set
		if(token == null)
			return null;
		else if(token.equals("#"))
			return TokenType.NUMBER_SYMBOL;
		else if(token.equals(";"))
			return TokenType.SEMI_COLON;
		else if(token.equals("("))
			return TokenType.OPEN_PARENT;
		else if(token.equals(")"))
			return TokenType.CLOSE_PARENT;
		else if(token.equals(","))
			return TokenType.COMMA;
		else if(token.equals("="))
			return TokenType.ASSIGN;
		else if(token.equals(">!"))
			return TokenType.STORE;
		else if(token.equals("begin")||token.equals("end")||token.equals("in")||token.equals("with"))
			return TokenType.KEYWORD;
		else if(token.equals("replace")||token.equals("recursivereplace")||
				token.equals("print")||token.equals("find")||token.equals("maxfreqstring"))
			return TokenType.FUNCTION;
		else if(token.equals("diff")||
				token.equals("union")||token.equals("inters"))
			return TokenType.SPECIAL;
		else 
			return TokenType.ID;
	}
	
	public int peekTokenLineNumber() {
		return lineNumber;
	}
	
	public String determineNextToken() {
		if(!(lineNumber<totalFileLines || columnNumber<currentLine.length)){
			return nextToken=null;
		}
		if((currentLine==null || columnNumber>=currentLine.length) && fileReader.hasNextLine()) {
			currentLine = fileReader.nextLine().toCharArray();
			columnNumber = 0;
			lineNumber++;
			if(!parenStack.isEmpty()) {
				System.out.println("Syntax Error: Unbalanced Parenthesis, maybe you are missing ). \nLine: " + (lineNumber-1));
				System.exit(0);
			}
		}
		//skip empty lines
		while(currentLine.length==0 && fileReader.hasNextLine()) {
			currentLine = fileReader.nextLine().toCharArray();
			columnNumber = 0;
			lineNumber++;
		}
		if(!(lineNumber<totalFileLines || columnNumber<currentLine.length))
			return nextToken = null;
		//special cases
		if(columnNumber<currentLine.length && !(scanningRegex || scanningFilenames)) {
			if(SPECIALCASES.contains(currentLine[columnNumber]+"")) {
				if(currentLine[columnNumber]=='(')
					parenStack.push('(');
				if(currentLine[columnNumber]==')') {
					try {
						parenStack.pop();
					} catch(Exception e) {
						System.out.println("Syntax Error: Unbalanced Parenthesis, maybe you are missing (. \nLine: " + lineNumber + " \nColumn: " + (columnNumber-nextToken.length()) + " \nToken: " + currentLine[columnNumber]);
						System.exit(0);
					}
				}
				nextToken = currentLine[columnNumber]+"";
				columnNumber++;
				while(columnNumber<currentLine.length && !tokenHasMoreChars() && (!SPECIALCASES.contains(currentLine[columnNumber]+"") || (scanningRegex || scanningFilenames))) {
					columnNumber++;
				}
				nextTokenType = checkTokenType(nextToken);
				return nextToken;
			}
			
		}
		
		//normal case
		while(columnNumber<currentLine.length && tokenHasMoreChars() && (!SPECIALCASES.contains(currentLine[columnNumber]+"") || (scanningRegex || scanningFilenames))) {
			if(currentLine[columnNumber]=='\'') {
				scanningRegex=!scanningRegex;
				nextTokenType = TokenType.REGEX;
			}
			else if(currentLine[columnNumber]=='"') {
				scanningFilenames=!scanningFilenames;
				nextTokenType = TokenType.STRING;
			} 
			else {
			strBuffer.append(currentLine[columnNumber]);
			//constantly guess token type
			nextTokenType = checkTokenType(strBuffer.toString());
			}
			columnNumber++;
		}
		while(columnNumber<currentLine.length && !tokenHasMoreChars() && (!SPECIALCASES.contains(currentLine[columnNumber]+"") || (scanningRegex || scanningFilenames))) {
			columnNumber++;
		}
		nextToken = strBuffer.toString();
		strBuffer.delete(0,strBuffer.length());
		//check for syntax errors
		if(nextTokenType == TokenType.ID) {
			if(!Character.isLetter(nextToken.charAt(0))) {
				System.out.println("Syntax Error: ID variables must begin with a letter. \nLine: " + lineNumber + " \nColumn: " + (columnNumber-nextToken.length()) + " \nToken: " + nextToken);
				System.exit(0);
			}
			for(char ch : nextToken.toCharArray()) {
				if(!Character.isLetterOrDigit(ch) && ch!='_') {
					System.out.println("Syntax Error: ID variable contains invalid character. \nLine: " + lineNumber + " \nColumn: " + (columnNumber-nextToken.length()) + " \nToken: " + nextToken + " \nCharacter: " + ch);
					System.exit(0);
				}
			}
		}
		if(nextTokenType == TokenType.REGEX && !RegexValidator.validate(nextToken)) {
			System.out.println("Syntax Error: Invalid Regex. \nLine: " + lineNumber + " \nColumn: " + (columnNumber-nextToken.length()) + " \nToken: " + nextToken);
			System.exit(0);
		}
		return nextToken;
	}
	
	private boolean tokenHasMoreChars() {
		return (scanningRegex || scanningFilenames ) || currentLine[columnNumber]!='\n' && currentLine[columnNumber]!=';'  && currentLine[columnNumber]!=' ';
	}
	
	public boolean hasNextToken() {
		//return lineNumber<totalFileLines || columnNumber<currentLine.length;
		return nextToken!=null;
	}
	
	public String getCurrentLine(){
		return new StringBuffer().append(currentLine).toString();
	}
}
